//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
//  File:  flash.c
//  Programmer: Visual.Wei (2007)
//
//  This file implements boot loader functions related to image flash.
//
#include <eboot.h>
#include <s3c2443.h>
#include <config.h>
#include <VFLBuffer.h>
#include <WMRTypes.h>
#include <WMRconfig.h>
#include <FTL.h>
#include <VFL.h>
#include <FIL.h>
#include <WMR_bootpart.h>
#include <ll_nand.h>
//------------------------------------------------------------------------------
//  Local Functions


BOOL WriteFlashEBOOT(UINT32 address, UINT32 size);
static BOOL WriteFlashCE(UINT32 address, UINT32 size);
static VOID DumpTOC(ROMHDR *pTOC);

//------------------------------------------------------------------------------
//  Local Variables
#define FBLKSZ 0x20000
#define FSECSZ 0x200
#define FSPERB (FBLKSZ/FSECSZ)
//------------------------------------------------------------------------------

VOID DumpTOC(ROMHDR *pTOC)
{
	// Print out ROMHDR information
	OALMSG(OAL_INFO, (L"\r\n"));
	OALMSG(OAL_INFO, (L"ROMHDR (pTOC = 0x%08x) ---------------------\r\n", pTOC));
	OALMSG(OAL_INFO, (L"  DLL First           : 0x%08x\r\n", pTOC->dllfirst));
	OALMSG(OAL_INFO, (L"  DLL Last            : 0x%08x\r\n", pTOC->dlllast));
	OALMSG(OAL_INFO, (L"  Physical First      : 0x%08x\r\n", pTOC->physfirst));
	OALMSG(OAL_INFO, (L"  Physical Last       : 0x%08x\r\n", pTOC->physlast));
	OALMSG(OAL_INFO, (L"  Num Modules         : %10d\r\n", pTOC->nummods));
	OALMSG(OAL_INFO, (L"  RAM Start           : 0x%08x\r\n", pTOC->ulRAMStart));
	OALMSG(OAL_INFO, (L"  RAM Free            : 0x%08x\r\n", pTOC->ulRAMFree));
	OALMSG(OAL_INFO, (L"  RAM End             : 0x%08x\r\n", pTOC->ulRAMEnd));
	OALMSG(OAL_INFO, (L"  Num Copy Entries    : %10d\r\n", pTOC->ulCopyEntries));
	OALMSG(OAL_INFO, (L"  Copy Entries Offset : 0x%08x\r\n", pTOC->ulCopyOffset));
	OALMSG(OAL_INFO, (L"  Prof Symbol Length  : 0x%08x\r\n", pTOC->ulProfileLen));
	OALMSG(OAL_INFO, (L"  Prof Symbol Offset  : 0x%08x\r\n", pTOC->ulProfileOffset));
	OALMSG(OAL_INFO, (L"  Num Files           : %10d\r\n", pTOC->numfiles));
	OALMSG(OAL_INFO, (L"  Kernel Flags        : 0x%08x\r\n", pTOC->ulKernelFlags));
	OALMSG(OAL_INFO, (L"  FileSys RAM Percent : 0x%08x\r\n", pTOC->ulFSRamPercent));
	OALMSG(OAL_INFO, (L"  Driver Glob Start   : 0x%08x\r\n", pTOC->ulDrivglobStart));
	OALMSG(OAL_INFO, (L"  Driver Glob Length  : 0x%08x\r\n", pTOC->ulDrivglobLen));
	OALMSG(OAL_INFO, (L"  CPU                 :     0x%04x\r\n", pTOC->usCPUType));
	OALMSG(OAL_INFO, (L"  MiscFlags           :     0x%04x\r\n", pTOC->usMiscFlags));
	OALMSG(OAL_INFO, (L"  Extensions          : 0x%08x\r\n", pTOC->pExtensions));
	OALMSG(OAL_INFO, (L"  Tracking Mem Start  : 0x%08x\r\n", pTOC->ulTrackingStart));
	OALMSG(OAL_INFO, (L"  Tracking Mem Length : 0x%08x\r\n", pTOC->ulTrackingLen));
	OALMSG(OAL_INFO, (L"------------------------------------------------\r\n"));
	OALMSG(OAL_INFO, (L"\r\n"));
}

static ROMHDR * TOCbase(UINT32 address)
{
	UINT32 *pInfo;
	// Verify that we get CE image.
	pInfo = (UINT32*)(address + ROM_SIGNATURE_OFFSET);
	if (*pInfo++ != ROM_SIGNATURE)
	{
		OALMSG(OAL_ERROR, (L"ERROR: OEMWriteFlash: "
		                   L"Image Signature not found\r\n"
		                  ));
		return NULL;
	}
	pInfo++;

	// We are on correct location....
	return (ROMHDR*)((UINT32) * pInfo + address);
}


BOOL WriteFlashEBOOT(UINT32 address, UINT32 size)
{
	ROMHDR *pTOC;
	UINT8 *pData;
	volatile S3C2443_IOPORT_REG *s2443IOP = (S3C2443_IOPORT_REG *)OALPAtoVA(S3C2443_BASE_REG_PA_IOPORT, FALSE);
	DWORD block,page,dwNumBlock;
	// Get data location
	pData = OEMMapMemAddr(address, address);

	// We are on correct location....
	pTOC = TOCbase(0x30038000);

	if(pTOC->physfirst!=0x30038000)
	{
		OALMSG(OAL_ERROR,(_T("Error: Eboot not found\r\n")));
		goto cleanUp;
	}

	// Let see
	DumpTOC(pTOC);
	//GPB5,write protect disable
	s2443IOP->GPBCON = (s2443IOP->GPBCON & ~(0x3<<10)) | (0x1<<10);
	s2443IOP->GPBDAT = s2443IOP->GPBDAT | (1<<5);
	s2443IOP->GPBUDP = (s2443IOP->GPBUDP & ~(0x3<<10)) | (0x1<<10);

	if (!FMD_Init (NULL, NULL, NULL))
	{
		RETAILMSG(1, (L" WMR_Init is failed.\r\n"));
		goto cleanUp;
	}

	block=2;
	dwNumBlock=2;
	while(dwNumBlock>0)
	{
		NAND_EraseBlock(block);
		for (page=0;page<NAND_BLOCK_PAGE; page++)
		{
			NAND_WritePage(block, page, pData+NAND_PAGE_SIZE*page);
			OALMSG(OAL_INFO,(_T("$")));
		}
		block++;
		dwNumBlock--;
		pData+= NAND_BLOCK_PAGE*NAND_PAGE_SIZE;

	}
	OALMSG(OAL_INFO,(_T("Eboot write OK\r\n")));
cleanUp:
	return TRUE;
}


BOOL WriteFlashBootloader(UINT32 address, UINT32 size)
{
	UINT8 *pData;
	volatile S3C2443_IOPORT_REG *s2443IOP = (S3C2443_IOPORT_REG *)OALPAtoVA(S3C2443_BASE_REG_PA_IOPORT, FALSE);
	DWORD block,page,dwNumBlock;
	// Get data location
	pData = OEMMapMemAddr(address, address);

	//GPB5,write protect disable
	s2443IOP->GPBCON = (s2443IOP->GPBCON & ~(0x3<<10)) | (0x1<<10);
	s2443IOP->GPBDAT = s2443IOP->GPBDAT | (1<<5);
	s2443IOP->GPBUDP = (s2443IOP->GPBUDP & ~(0x3<<10)) | (0x1<<10);

	if (!FMD_Init (NULL, NULL, NULL))
	{
		RETAILMSG(1, (L" WMR_Init is failed.\r\n"));
		goto cleanUp;
	}

	block=0;
	dwNumBlock=size/(NAND_BLOCK_PAGE*NAND_PAGE_SIZE);
	while(dwNumBlock>0)
	{
		NAND_EraseBlock(block);
		for (page=0;page<NAND_BLOCK_PAGE; page++)
		{
			NAND_WritePage(block, page, pData+NAND_PAGE_SIZE*page);
			OALMSG(OAL_INFO,(_T("$")));
		}
		block++;
		dwNumBlock--;
		pData+= NAND_BLOCK_PAGE*NAND_PAGE_SIZE;

	}
	OALMSG(OAL_INFO,(_T("bootloader write OK\r\n")));
cleanUp:
	return TRUE;
}

BOOL WriteFlashCE(UINT32 address, UINT32 size)
{
	UINT8* pData,*pSrcData;
	volatile DWORD sector,totalblock;
	volatile DWORD iSector;
	DWORD trytime;

	totalblock=size/(FSPERB*(FSECSZ+8));
	OALMSG(OAL_INFO, (TEXT("CE size: 0x%x\r\n"),size));

	pSrcData=pData= OEMMapMemAddr(address,address);
	iSector=0;
	sector=0;

	while(totalblock>0)
	{
		if  (sector <= USER_SECTORS_TOTAL)
		{
#if 0
			for (iSector=0; iSector<FSPERB; iSector++)
			{
				//for 3rd part OEM use,flash.bin
				memcpy(pData+(iSector*FSECSZ), pSrcData+(iSector*(FSECSZ+8)), FSECSZ);
			}
#else
			iSector=FSPERB;
#endif
			trytime=10;
ce_retry:
			if (FTL_Write(sector, FSPERB, pData) != FTL_SUCCESS)
			{
				OALMSG(OAL_ERROR, (TEXT("WriteData: failed to write sector (0x%x).\r\n"), sector));
				if(trytime>0)
				{
					trytime--;
					goto ce_retry;
				}
				return FALSE;
			}
			else
			{
				OALMSG(OAL_INFO, (TEXT("$")));
			}

			pData+=(FSPERB*FSECSZ);
			pSrcData+=(FSPERB*(FSECSZ+8));
			sector+=iSector;
		}
		totalblock--;
	}

	return TRUE;
}
//------------------------------------------------------------------------------
//
//  Function:  OEMStartEraseFlash
//
//  This function is called by the bootloader to initiate the flash memory
//  erasing process.
//
BOOL OEMStartEraseFlash(ULONG address, ULONG size)
{
	//BYTE buffer[0x20000];
	//volatile DWORD sector,totalsector;
	volatile S3C2443_IOPORT_REG *s2443IOP = (S3C2443_IOPORT_REG *)OALPAtoVA(S3C2443_BASE_REG_PA_IOPORT, FALSE);
	//GPB5,write protect disable
	s2443IOP->GPBCON = (s2443IOP->GPBCON & ~(0x3<<10)) | (0x1<<10);
	s2443IOP->GPBDAT = s2443IOP->GPBDAT | (1<<5);
	s2443IOP->GPBUDP = (s2443IOP->GPBUDP & ~(0x3<<10)) | (0x1<<10);

	if (!FMD_Init (NULL, NULL, NULL))
	{
		RETAILMSG(1, (L" WMR_Init is failed.\r\n"));
		return FALSE;
	}
#if 0
	if(g_eboot.type==DOWNLOAD_TYPE_BINDIO)
	{
		//fill 0xFF to FTL partition
		OALMSG(OAL_INFO, (TEXT("Fill 0xFF to FTL \r\n")));
		sector=0;
		totalsector=sizeof(buffer)/(FSECSZ);
		memset(buffer, 0xFF,sizeof(buffer));
		while ( (sector <= USER_SECTORS_TOTAL)&&(sector<(size/(FSECSZ+8))))
		{
			if (FTL_Write(sector, totalsector, buffer) != FTL_SUCCESS)
			{
				OALMSG(OAL_ERROR, (TEXT("WriteData: failed to write sector (0x%x).\r\n"), sector));
				return FALSE;
			}
			else
			{
				OALMSG(OAL_INFO, (TEXT("&")));
			}
			sector+=totalsector;
		}
	}
#endif	
	return TRUE;
}

VOID OEMContinueEraseFlash()
{
#if 0
	UINT32 dwRecOffset,dwRecLen;
	UINT8* pData;
	volatile DWORD sector,totalsector;
	DWORD iSector;

	//for flash.bin only
	if(g_eboot.type!=DOWNLOAD_TYPE_BINDIO)
		return;

	//Get Record Infomation
	dwRecOffset=g_eboot.recordOffset;
	dwRecLen=g_eboot.readSize;
	pData=g_eboot.pReadBuffer;


	if(dwRecLen%(FSECSZ+8))
	{
		OALMSG(OAL_ERROR,(_T("error: plz. check memory.cfg.xml \r\n")));
		return;
	}
	else
		totalsector=dwRecLen/(FSECSZ+8);

	//calc start sector number
	sector = dwRecOffset / (FSECSZ+8);

	//OALMSG(OAL_INFO, (TEXT("Sector=%d seclen=0x%x\r\n"), sector,dwRecLen));


	if  (sector <= USER_SECTORS_TOTAL)
	{

		for (iSector=0; iSector<totalsector; iSector++)
		{
			memcpy(pData+iSector*FSECSZ, pData+iSector*(FSECSZ+8), FSECSZ);
		}

		if (FTL_Write(sector, totalsector, pData) != FTL_SUCCESS)
		{
			OALMSG(OAL_ERROR, (TEXT("WriteData: failed to write sector (0x%x).\r\n"), sector));
			return;
		}
		else
		{
			OALMSG(OAL_INFO, (TEXT("$")));
		}

	}
#endif
}

BOOL
OEMFinishEraseFlash()
{
	BOOL rc = TRUE;
	if(g_eboot.type==DOWNLOAD_TYPE_BINDIO)
		OALMSG(OAL_INFO, (TEXT("flash.bin write OK\r\n")));
	return rc;
}

BOOL
OEMWriteFlash(ULONG address, ULONG size)
{
	BOOL rc;
	OALMSG(OAL_FUNC, (L"+OEMWriteFlash(0x%08x)\r\n", address));
	printk(L"Writing Flash... (0x%08x)\r\n", address);

	switch (g_eboot.type)
	{
	case DOWNLOAD_TYPE_BINDIO:
		rc = WriteFlashCE(address, size);
		//rc = TRUE;
		break;
	case DOWNLOAD_TYPE_EBOOT:
		rc = WriteFlashEBOOT(address, size);
		break;
	case DOWNLOAD_TYPE_BOOTLOADER:
		rc = WriteFlashBootloader(address, size);
		break;

	default:
		rc = FALSE;
		break;
	}
	printk(L"completed\r\n");
	return rc;
}
